package com.zly;

import com.zly.entity.DistanceCalculateResult;
import com.zly.entity.ExecuteRecord;
import com.zly.entity.parameter.FunctionReturnValueParameter;

import com.zly.functions.ExtraData;
import com.zly.functions.Register;
import org.apache.commons.lang3.ClassUtils;

import java.lang.reflect.*;
import java.util.*;

public class FunctionManager {


    //函数比较器，Class对象的getMethods()方法返回的方法数组是乱序的，多次调用顺序不一致，用该函数比较器进行排序确保顺序一致
    private static final Comparator<Method> METHOD_COMPARATOR;
    //函数参数的合法基本类型的数组类型
    private static final Set<Class<?>> LEGAL_PRIMITIVE_ARRAY_TYPES;
    //函数参数的合法包装类型的数组类型
    private static final Set<Class<?>> LEGAL_WRAPPER_ARRAY_TYPES;
    //函数参数及返回值的合法类型集合
    private static final Set<Class<?>> LEGAL_PARAMETER_TYPES;

    //键为方法名，值为所有同名方法的列表（参数列表不同）
    private final Map<String, List<Method>> methodMap;
    //是否记录函数的执行，默认记录
    private final boolean recordFunctionExecute;
    //记录函数执行的映射
    private final Map<Method, List<ExecuteRecord>> executeRecordMap;
    //
    private DistanceCalculateResult curDistanceCalculateResult;

    static{

        LEGAL_PRIMITIVE_ARRAY_TYPES = new HashSet<>();{
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(boolean[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(char[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(byte[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(int[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(short[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(long[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(double[].class);
            LEGAL_PRIMITIVE_ARRAY_TYPES.add(float[].class);
        }

        LEGAL_WRAPPER_ARRAY_TYPES = new HashSet<>();{
            LEGAL_WRAPPER_ARRAY_TYPES.add(Boolean[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Character[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Byte[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Integer[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Short[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Long[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Double[].class);
            LEGAL_WRAPPER_ARRAY_TYPES.add(Float[].class);
        }

        LEGAL_PARAMETER_TYPES = new HashSet<>();{

            //添加合法类型
            LEGAL_PARAMETER_TYPES.add(String.class);
            LEGAL_PARAMETER_TYPES.add(Object.class);
            LEGAL_PARAMETER_TYPES.add(Boolean.class);
            LEGAL_PARAMETER_TYPES.add(boolean.class);
            LEGAL_PARAMETER_TYPES.add(Character.class);
            LEGAL_PARAMETER_TYPES.add(char.class);
            LEGAL_PARAMETER_TYPES.add(Byte.class);
            LEGAL_PARAMETER_TYPES.add(byte.class);
            LEGAL_PARAMETER_TYPES.add(Integer.class);
            LEGAL_PARAMETER_TYPES.add(int.class);
            LEGAL_PARAMETER_TYPES.add(Short.class);
            LEGAL_PARAMETER_TYPES.add(short.class);
            LEGAL_PARAMETER_TYPES.add(Long.class);
            LEGAL_PARAMETER_TYPES.add(long.class);
            LEGAL_PARAMETER_TYPES.add(Double.class);
            LEGAL_PARAMETER_TYPES.add(double.class);
            LEGAL_PARAMETER_TYPES.add(Float.class);
            LEGAL_PARAMETER_TYPES.add(float.class);

            //合法的数组类型
            LEGAL_PARAMETER_TYPES.add(String[].class);
            LEGAL_PARAMETER_TYPES.add(Object[].class);

            LEGAL_PARAMETER_TYPES.addAll(LEGAL_PRIMITIVE_ARRAY_TYPES);
            LEGAL_PARAMETER_TYPES.addAll(LEGAL_WRAPPER_ARRAY_TYPES);

        }

        METHOD_COMPARATOR = Comparator.comparing(m -> Arrays.toString(m.getParameterTypes()));

    }

    //构造
    public FunctionManager(){
        //默认开启记录函数执行
        this(true);
    }

    //构造
    public FunctionManager(boolean recordFunctionExecute){
        this.recordFunctionExecute = recordFunctionExecute;
        methodMap = new HashMap<>();
        executeRecordMap = new HashMap<>();
    }

    //根据Method对象注册函数
    public void registerFunction(Method method){

        //若验证通过
        if(validateFunction(method)){

            String methodName = method.getName();
            if(!methodMap.containsKey(methodName)){
                methodMap.put(methodName, new ArrayList<>());
                methodMap.get(methodName).add(method);
            }else{

                boolean overrode = false;//是否发生覆盖
                List<Method> methodList = methodMap.get(methodName);
                for(int i = 0; i < methodList.size(); i++){
                    //若发现已有相同参数列表的方法，覆盖
                    if(Arrays.equals(methodList.get(i).getParameterTypes(), method.getParameterTypes())){
                        overrode = true;
                        methodList.set(i, method);
                        break;
                    }
                }
                if(!overrode){//若未发生覆盖
                    methodList.add(method);
                }

            }
        }else{
            throw new RuntimeException("函数的参数或返回值存在不支持的类型:" + method);
        }

    }

    //根据函数提供类注册函数
    public void registerFunction(Class<?> functionProviderClass){

        for(Method method : functionProviderClass.getMethods()){

            var modifiers = method.getModifiers();

            //仅添加共有静态方法，且必须带有@Register注释
            if(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers) && method.isAnnotationPresent(Register.class)){
                registerFunction(method);
            }
        }

        //排序，Class对象的getMethods()方法返回的方法数组元素顺序是无保证的，多次调用顺序不一致。为防止最佳匹配有多个时，多次调用所匹配的方法不一致，需要进行排序
        for(List<Method> methodList : methodMap.values()){
            methodList.sort(METHOD_COMPARATOR);
        }

    }

    //匹配FunctionReturnValueParameter对应的方法
    public Method bestMatch(FunctionReturnValueParameter parameter){
        return bestMatch(parameter.getFunctionName(), parameter.getFunctionArgs());
    }

    //匹配函数名与参数列表对应的方法
    public Method bestMatch(String functionName, Object[] functionArgs){
        //注意当FunctionReturnValueParameter没有参数列表时，其functionArgs为长度为0的空数组，而不为null
        Class<?>[] argsTypes = new Class[functionArgs.length];
        for(int i = 0; i < functionArgs.length; i++){
            argsTypes[i] = functionArgs[i].getClass();
        }
        return bestMatch(functionName, argsTypes);
    }

    //匹配函数名与参数列表对应的方法
    public Method bestMatch(String functionName, Class<?>[] argsTypes){

        Method function = null;
        if(methodMap.containsKey(functionName)){

            DistanceCalculateResult bestResult = DistanceCalculateResult.worstResult();
            Method bestMatchMethod = null;

            for(Method method : methodMap.get(functionName)){

                DistanceCalculateResult result = calculateDistance(argsTypes, method.getParameterTypes());
                if(result.betterThan(bestResult)){
                    bestResult = result;
                    bestMatchMethod = method;
                }

            }

            curDistanceCalculateResult = bestResult;

            if(bestResult.betterThan(DistanceCalculateResult.worstResult())){
                function = bestMatchMethod;
            }else{
                throw new RuntimeException("匹配名为" + functionName + "的函数失败:参数列表" + Arrays.toString(argsTypes) + "匹配失败");
            }

        }else{
            throw new RuntimeException("匹配名为" + functionName + "的函数失败:没有名称为" + functionName + "的函数");
        }

        return function;

    }

    //根据函数名和参数列表执行函数
    public Object executeFunction(String functionName, Object[] functionArgs){
        return executeFunction(bestMatch(functionName,functionArgs), functionArgs);
    }

    //根据FunctionReturnValueParameter执行函数
    public Object executeFunction(FunctionReturnValueParameter fp){

        if(fp.getMethod() == null){
            fp.matchMethod(this);
        }

        return executeFunction(fp.getMethod(), fp.getFunctionArgs());

    }

    //查询该函数执行记录
    public List<ExecuteRecord> queryExecuteRecord(Method method){
        return executeRecordMap.get(method);
    }

    //清除所有函数执行记录
    public void clearExecuteRecord() {
        executeRecordMap.clear();
    }

    //检查某个类型是否为合法参数类型
    public static boolean isValidParameterType(Class<?> type){
        return LEGAL_PARAMETER_TYPES.contains(type);
    }

    //根据Method和参数列表执行函数，本类所有executeFunction最终执行的都是该函数
    private Object executeFunction(Method method, Object[] functionArgs){

        autoTypeConvert(functionArgs);

        try {

            Object result = method.invoke(null, functionArgs);

            //记录函数执行
            if(recordFunctionExecute){

                ExecuteRecord record = null;{

                    //记录参数列表和返回值
                    record = new ExecuteRecord(functionArgs, result);

                    //记录额外信息
                    if(method.isAnnotationPresent(ExtraData.class)){

                        ExtraData extraData = method.getAnnotation(ExtraData.class);

                        for(String fieldName : extraData.fieldNames()){

                            Field field = findFieldRecursive(method.getDeclaringClass(), fieldName);
                            if(field != null){

                                field.setAccessible(true);
                                Object value = field.get(null);
                                record.putExtraData(fieldName, value);

                            }else{
                                throw new RuntimeException("找不到函数" + method + "ExtraData注解中的静态字段:" + fieldName);
                            }

                        }
                    }

                }

                if(!executeRecordMap.containsKey(method)){
                    executeRecordMap.put(method,new ArrayList<>());
                }
                executeRecordMap.get(method).add(record);

            }

            return result;

        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    //如需要，进行自动类型转换。method.invoke方法本身可自动装拆箱，但不支持数组的装拆箱，本方法进行数组的装拆箱
    private void autoTypeConvert(Object[] functionArgs){

        if(curDistanceCalculateResult.existArrayWrap()){

            for(int arrayArgIndex : curDistanceCalculateResult.wrapIndexList()){

                //此arg必为数组类型
                Object arg = functionArgs[arrayArgIndex];

                Class<?> elemType = arg.getClass().getComponentType();
                Class<?> convertedType = elemType.isPrimitive() ? ClassUtils.primitiveToWrapper(elemType) : ClassUtils.wrapperToPrimitive(elemType);

                int len = Array.getLength(arg);
                var convertedArray = Array.newInstance(convertedType, len);
                for(int i = 0; i < len; i++){
                    Array.set(convertedArray, i, Array.get(arg, i));
                }

                functionArgs[arrayArgIndex] = convertedArray;

            }

        }

    }

    //递归查找字段，仅查找静态字段，查找失败则返回null
    private static Field findFieldRecursive(Class<?> clazz, String fieldName){

        Field targetField = null;
        //尝试在本类查找
        for(Field field : clazz.getDeclaredFields()){
            //仅查找静态字段
            if(Modifier.isStatic(field.getModifiers())){
                if(field.getName().equals(fieldName)){
                    targetField = field;
                    break;
                }
            }
        }

        //若在本类成功找到，或未成功找到且当前类不存在父类
        if(targetField != null || clazz.getSuperclass() == null){
            return targetField;
        }else{//若在本类未找到，且当前类存在父类，递归查找
            return findFieldRecursive(clazz.getSuperclass(), fieldName);
        }

    }

    //检查要注册的函数是否满足要求
    private static boolean validateFunction(Method method){

        //检查返回值类型
        if(!isValidReturnType(method.getReturnType())){
            return false;
        }

        //检查参数类型
        for(Class<?> type : method.getParameterTypes()){
            if(!isValidParameterType(type)){
                return false;
            }
        }

        return true;

    }

    //检查某个类型是否为合法返回值类型，返回值支持void类型
    private static boolean isValidReturnType(Class<?> type){ return type.equals(void.class) || isValidParameterType(type); }

    //计算两个参数列表的“距离”
    private static DistanceCalculateResult calculateDistance(Class<?>[] argTypes, Class<?>[] paramTypes){

        if(argTypes.length != paramTypes.length){ //若参数列表长度不同，距离为最大
            return DistanceCalculateResult.worstResult();
        }

        if(argTypes.length == 0){ //若两参数列表长度都为0，距离最小
            return DistanceCalculateResult.bestResult();
        }

        boolean existArrayWrap = false;
        List<Integer> wrapIndexList = null;

        int distance = 0;
        for(int i = 0; i < argTypes.length; i++){

            Class<?> typeA = argTypes[i];
            Class<?> typeB = paramTypes[i];

            if(typeA != typeB){//若两参数列表某处类型不同

                boolean arrayCompare = false;

                //若两参数均为数组类型，接下来比较其元素类型距离
                if(typeA.getComponentType() != null && typeB.getComponentType() != null){
                    typeA = typeA.getComponentType();
                    typeB = typeB.getComponentType();
                    arrayCompare = true;
                }

                if(isPrimitiveAndWrapper(typeA, typeB)){//若两参数列表某处的类型一个为基本数据类型一个为其包装类

                    distance += DistanceCalculateResult.WRAP_DISTANCE;

                    if(arrayCompare){
                        existArrayWrap = true;
                        if(wrapIndexList == null){
                            wrapIndexList = new ArrayList<>();
                        }
                        wrapIndexList.add(i);
                    }

                }else if(typeB == Object.class){//若函数的参数为Object类型
                    distance += DistanceCalculateResult.OBJECT_DISTANCE;
                }
                else{
                    distance = DistanceCalculateResult.MAX_DISTANCE;
                    break;
                }

            }
        }

        return existArrayWrap ? new DistanceCalculateResult(distance, wrapIndexList) :  new DistanceCalculateResult(distance);

    }

    //判断两个类型是否一个为基本数据类型另一个为其包装类
    private static boolean isPrimitiveAndWrapper(Class<?> typeA, Class<?> typeB){

        //若两类型相同，显然不满足要求
        if(typeA == typeB){
            return false;
        }

        //若两类型只有一个为基本数据类型
        if(typeA.isPrimitive() ^ typeB.isPrimitive()){

            Class<?> primitive = null;
            Class<?> wrapper = null;
            if(typeA.isPrimitive()){
                primitive = typeA;
                wrapper = typeB;
            }else{
                primitive = typeB;
                wrapper = typeA;
            }

            return ClassUtils.primitiveToWrapper(primitive).equals(wrapper);

        }else{//若两类型都为或都不为基本数据类型
            return false;
        }

    }

}
